<template>
  <div ref="wrapRef" :class="getWrapperClass">
    <BasicForm
      ref="formRef"
      submitOnReset
      v-bind="getFormProps"
      :hideLabel="searchForm?.hideLabel"
      :baseColProps="
        getBindValues?.formConfig?.baseColProps ?? {
          xs: 12,
          sm: 8,
          md: 6,
          lg: 6,
          xl: 6,
          xxl: 6,
        }
      "
      v-if="getBindValues.useSearchForm"
      :tableAction="tableAction"
      @register="registerForm"
      @submit="handleSearchInfoChange"
      @advanced-change="redoHeight"
    >
      <template
        #[replaceFormSlotKey(item)]="data"
        v-for="item in getFormSlotKeys"
      >
        <slot :name="item" v-bind="data || {}"></slot>
      </template>
    </BasicForm>
    <Table
      ref="tableElRef"
      v-bind="getBindValues"
      :rowClassName="getRowClassName"
      v-show="getEmptyDataIsShowTable"
      @change="handleTableChange"
    >
      <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
        <template v-if="item === 'action'">
          <div class="flex items-center">
            <slot :name="item" v-bind="data || {}"></slot>
            <TableAction
              :actions="[
                {
                  auth: getOptions?.detail?.permission || '',
                  label: '查看',
                  ifShow: !!getOptions?.detail?.show,
                  disabled: getOptions?.detail?.disabled
                    ? getOptions?.detail?.disabled(data)
                    : false,
                  onClick: () => handleAction('detail', data),
                },
                {
                  auth: getOptions?.edit?.permission || '',
                  label: getOptions?.edit?.label
                    ? typeof getOptions?.edit?.label === 'string'
                      ? getOptions?.edit?.label
                      : getOptions?.edit?.label(data)
                    : '编辑',
                  ifShow: !!getOptions?.edit && (typeof getOptions?.edit?.show === 'boolean'
                      ? getOptions?.edit?.show
                      : getOptions?.edit?.show(data)),
                  disabled: getOptions?.edit?.disabled
                    ? getOptions?.edit?.disabled(data)
                    : false,
                  onClick: () => handleAction('edit', data),
                },
                {
                  auth: getOptions?.delete?.permission || '',
                  color: 'error',
                  label: '删除',
                  ifShow: !!getOptions?.delete?.show,
                  disabled: getOptions?.delete?.disabled
                    ? getOptions?.delete?.disabled(data)
                    : false,
                  popConfirm: {
                    title: '是否确认删除',
                    confirm: () => handleAction('remove', data),
                  },
                },
              ]"
            />
          </div>
        </template>
        <template v-else>
          <slot :name="item" v-bind="data || {}"></slot>
        </template>
      </template>
      <template #headerCell="{ column }">
        <HeaderCell :column="column" />
      </template>
      <!-- 增加对antdv3.x兼容 -->
      <template #bodyCell="data">
        <slot name="bodyCell" v-bind="data || {}"></slot>
      </template>
      <!--      <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">-->
      <!--        <HeaderCell :column="column" />-->
      <!--      </template>-->
    </Table>
  </div>
</template>
<script lang="ts">
import type {
  BasicTableProps,
  TableActionType,
  SizeType,
  ColumnChangeParam,
  TableOptions,
} from './types/table';
import componentSetting from '@/settings/componentSetting';

import {
  defineComponent,
  ref,
  computed,
  unref,
  toRaw,
  inject,
  watchEffect,
} from 'vue';
import { Table, Divider } from 'ant-design-vue';
import BasicForm from '@/components/Form/src/BasicForm.vue';
import { useForm } from '@/components/Form/src/hooks/useForm';
import { PageWrapperFixedHeightKey } from '@/enums/pageEnum';
import HeaderCell from './components/HeaderCell.vue';
import { InnerHandlers } from './types/table';
import type { FormSchema } from '@/components/Form';
import TableAction from '@/components/Table/src/components/TableAction.vue';

import { usePagination } from './hooks/usePagination';
import { useColumns } from './hooks/useColumns';
import { useDataSource } from './hooks/useDataSource';
import { useLoading } from './hooks/useLoading';
import { useRowSelection } from './hooks/useRowSelection';
import { useTableScroll } from './hooks/useTableScroll';
import { useTableScrollTo } from './hooks/useScrollTo';
import { useCustomRow } from './hooks/useCustomRow';
import { useTableStyle } from './hooks/useTableStyle';
import { useTableHeader } from './hooks/useTableHeader';
import { useTableExpand } from './hooks/useTableExpand';
import { createTableContext } from './hooks/useTableContext';
import { useTableFooter } from './hooks/useTableFooter';
import { useTableForm } from './hooks/useTableForm';
import { useDesign } from '@/hooks/web/useDesign';
import { useMessage } from '@/hooks/web/useMessage';

import { omit } from 'lodash-es';
import { basicProps } from './props';
import { isFunction } from '@/utils/is';
import { warn } from '@/utils/log';
import Sortable from 'sortablejs';
import { onMounted } from 'vue';
import { delData } from '@/utils/http/axios/component';

export default defineComponent({
  name: 'BasicTable',
  components: {
    Table,
    BasicForm,
    HeaderCell,
    TableAction,
    Divider,
  },
  props: basicProps,
  emits: [
    'fetch-success',
    'fetch-error',
    'selection-change',
    'register',
    'row-click',
    'row-dbClick',
    'row-contextmenu',
    'row-mouseenter',
    'row-mouseleave',
    'edit-end',
    'edit-cancel',
    'edit-row-end',
    'edit-change',
    'expanded-rows-change',
    'change',
    'columns-change',
    'crud-change',
    'row-drag-end',
  ],
  setup(props, { attrs, emit, slots, expose }) {
    const { table } = componentSetting;
    const searchForm = ref(table.searchForm);
    const tableElRef = ref(null);
    const tableData = ref<Recordable[]>([]);
    const { createMessage } = useMessage();

    const wrapRef = ref(null);
    const formRef = ref(null);
    const innerPropsRef = ref<Partial<BasicTableProps>>();

    const { prefixCls } = useDesign('basic-table');
    const [registerForm, formActions] = useForm();

    const getProps = computed(() => {
      return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
    });
    const getOptions = computed(() => {
      return unref(getProps).options as unknown as TableOptions;
    });
    const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
    watchEffect(() => {
      unref(isFixedHeightPage) &&
        props.canResize &&
        warn(
          "'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)"
        );
    });
    onMounted(() => {
      if (props.rowDrag) initSortable();
    });
    //初始化表格拖动
    const initSortable = () => {
      const table = unref(tableElRef) as unknown as ComponentRef;
      if (!table) return;
      const mytb = (table.$el as Element).querySelector('tbody') as HTMLElement;
      new Sortable(mytb, {
        handle: '.ant-table-row',
        animation: 150,
        ghostClass: 'blue-background-class',
        sort: true,
        async onEnd({ newIndex, oldIndex }) {
          const o = oldIndex - 1;
          const n = newIndex - 1;
          let dataSource = getDataSource();
          const elementToMove = dataSource.splice(o, 1)[0]; // 从索引2处删除一个元素，并将其存储在elementToMove中
          dataSource.splice(n, 0, elementToMove); // 将elementToMove插入到索引1的位置
          // 被排序元素 数据源
          emit('row-drag-end', elementToMove, n, dataSource);
        },
      });
    };
    const { getLoading, setLoading } = useLoading(getProps);
    const {
      getPaginationInfo,
      getPagination,
      setPagination,
      setShowPagination,
      getShowPagination,
    } = usePagination(getProps);

    const {
      getRowSelection,
      getRowSelectionRef,
      getSelectRows,
      setSelectedRows,
      clearSelectedRowKeys,
      getSelectRowKeys,
      deleteSelectRowByKey,
      setSelectedRowKeys,
    } = useRowSelection(getProps, tableData, emit);

    const {
      handleTableChange: onTableChange,
      getDataSourceRef,
      getDataSource,
      getRawDataSource,
      setTableData,
      updateTableDataRecord,
      deleteTableDataRecord,
      insertTableDataRecord,
      findTableDataRecord,
      fetch,
      getRowKey,
      reload,
      getAutoCreateKey,
      updateTableData,
    } = useDataSource(
      getProps,
      {
        tableData,
        getPaginationInfo,
        setLoading,
        setPagination,
        getFieldsValue: formActions.getFieldsValue,
        clearSelectedRowKeys,
        formRef,
      },
      emit
    );

    function handleTableChange(...args) {
      onTableChange.call(undefined, ...args);
      emit('change', ...args);
      // 解决通过useTable注册onChange时不起作用的问题
      const { onChange } = unref(getProps);
      onChange && isFunction(onChange) && onChange.call(undefined, ...args);
    }

    const {
      getViewColumns,
      getColumns,
      setCacheColumnsByField,
      setColumns,
      getColumnsRef,
      getCacheColumns,
    } = useColumns(getProps, getPaginationInfo);

    const { getScrollRef, redoHeight } = useTableScroll(
      getProps,
      tableElRef,
      getColumnsRef,
      getRowSelectionRef,
      getDataSourceRef,
      wrapRef,
      formRef
    );

    const { scrollTo } = useTableScrollTo(tableElRef, getDataSourceRef);

    const { customRow } = useCustomRow(getProps, {
      setSelectedRowKeys,
      getSelectRowKeys,
      clearSelectedRowKeys,
      getAutoCreateKey,
      emit,
    });

    const { getRowClassName } = useTableStyle(getProps, prefixCls);

    const { getExpandOption, expandAll, expandRows, collapseAll } =
      useTableExpand(getProps, tableData, emit);

    const handlers: InnerHandlers = {
      onColumnsChange: (data: ColumnChangeParam[]) => {
        emit('columns-change', data);
        // support useTable
        unref(getProps).onColumnsChange?.(data);
      },
      onAdvancedSearch: (params: Recordable) => {
        reload({
          searchInfo: params,
        });
      },
      onAdvancedSetDefault(schemas: FormSchema[]) {
        // formRef.value.getProps()
        formRef.value.setProps(
          {
            schemas,
          },
          true
        );
      },
      async onRemoveSuccess() {
        const { options } = unref(getProps);
        const deleteIds = getRowSelection().selectedRowKeys;
        options?.delete?.api && (await options.delete.api(deleteIds));
        createMessage.success('删除成功');
        reload();
        clearSelectedRowKeys();
      },
    };

    const { getHeaderProps } = useTableHeader(
      getProps,
      formRef,
      getRowSelection,
      slots,
      handlers
    );

    const { getFooterProps } = useTableFooter(
      getProps,
      getScrollRef,
      tableElRef,
      getDataSourceRef
    );

    const {
      getFormProps,
      replaceFormSlotKey,
      getFormSlotKeys,
      handleSearchInfoChange,
    } = useTableForm(getProps, slots, fetch, getLoading);
    const getBindValues = computed(() => {
      const dataSource = unref(getDataSourceRef);
      let propsData: Recordable = {
        ...attrs,
        customRow,
        ...unref(getProps),
        ...unref(getHeaderProps),
        scroll: unref(getScrollRef),
        loading: unref(getLoading),
        tableLayout: 'fixed',
        rowSelection: unref(getRowSelectionRef),
        rowKey: unref(getRowKey),
        columns: toRaw(unref(getViewColumns)),
        pagination: toRaw(unref(getPaginationInfo)),
        dataSource,
        footer: unref(getFooterProps),
        ...unref(getExpandOption),
      };
      // if (slots.expandedRowRender) {
      //   propsData = omit(propsData, 'scroll');
      // }

      propsData = omit(propsData, ['class', 'onChange']);
      return propsData;
    });

    const getWrapperClass = computed(() => {
      const values = unref(getBindValues);
      return [
        prefixCls,
        attrs.class,
        {
          [`${prefixCls}-form-container`]: values.useSearchForm,
          [`${prefixCls}--inset`]: values.inset,
        },
      ];
    });

    const getEmptyDataIsShowTable = computed(() => {
      const { emptyDataIsShowTable, useSearchForm } = unref(getProps);
      if (emptyDataIsShowTable || !useSearchForm) {
        return true;
      }
      return !!unref(getDataSourceRef).length;
    });

    function setProps(props: Partial<BasicTableProps>) {
      innerPropsRef.value = { ...unref(innerPropsRef), ...props };
    }

    const tableAction: TableActionType = {
      reload,
      getSelectRows,
      setSelectedRows,
      clearSelectedRowKeys,
      getSelectRowKeys,
      deleteSelectRowByKey,
      setPagination,
      setTableData,
      updateTableDataRecord,
      deleteTableDataRecord,
      insertTableDataRecord,
      findTableDataRecord,
      redoHeight,
      setSelectedRowKeys,
      setColumns,
      setLoading,
      getDataSource,
      getRawDataSource,
      setProps,
      getRowSelection,
      getPaginationRef: getPagination,
      getColumns,
      getCacheColumns,
      emit,
      updateTableData,
      setShowPagination,
      getShowPagination,
      setCacheColumnsByField,
      expandAll,
      expandRows,
      collapseAll,
      scrollTo,
      getSize: () => {
        return unref(getBindValues).size as SizeType;
      },
    };
    createTableContext({ ...tableAction, wrapRef, getBindValues });

    expose(tableAction);

    emit('register', tableAction, formActions);

    const handleAction = async (type: string, data: Recordable) => {
      const { record } = data;
      const options = unref(getProps)?.options as TableOptions;
      if (
        type === 'edit' &&
        !!options?.edit?.show &&
        options?.edit?.overFunction
      ) {
        options?.edit?.overFunction(data);
        emit('crud-change', type, record);
        return;
      }

      if (
        type === 'detail' &&
        !!options?.detail?.show &&
        options?.detail?.overFunction
      ) {
        options?.detail?.overFunction(data);
        emit('crud-change', type, record);
        return;
      }

      if (type === 'remove' && !!options?.delete?.show) {
        if (options.delete.before && isFunction(options.delete.before)) {
          if (!options.delete?.before(data)) {
            return;
          }
        }
        if (options?.delete?.overFunction) {
          options?.delete?.overFunction(data);
          return;
        }
        if (!options?.delete?.api) {
          createMessage.error('未配置删除api函数');
        } else {
          await delData(options?.delete?.api, [record.id]);
          createMessage.success('删除成功');
          reload();
          emit('crud-change', type, record);
        }
      }
    };

    return {
      formRef,
      tableElRef,
      getBindValues,
      getLoading,
      registerForm,
      handleSearchInfoChange,
      getEmptyDataIsShowTable,
      handleTableChange,
      getRowClassName,
      wrapRef,
      tableAction,
      redoHeight,
      getFormProps: getFormProps as any,
      replaceFormSlotKey,
      getFormSlotKeys,
      getWrapperClass,
      columns: getViewColumns,
      searchForm,
      handleAction,
      getOptions,
    };
  },
});
</script>
<style lang="less" scoped>
@border-color: #cecece4d;

@prefix-cls: ~'@{namespace}-basic-table';

[data-theme='dark'] {
  .ant-table-tbody > tr:hover.ant-table-row-selected > td,
  .ant-table-tbody > tr.ant-table-row-selected td {
    background-color: #262626;
  }
}

.@{prefix-cls} {
  max-width: 100%;
  height: 100%;

  &-row__striped {
    td {
      background-color: @app-content-background;
    }
  }

  &-form-container {
    // padding: 16px;

    .ant-form {
      width: 100%;
      // padding: 12px 12px 0 0;
      padding: 12px 10px 6px;

      background-color: @component-background;
      border-radius: 2px;
    }
  }

  .ant-table-cell {
    .ant-tag {
      margin-right: 0;
    }
  }

  .ant-table-wrapper {
    padding: 6px;
    background-color: @component-background;
    border-radius: 2px;

    .ant-table-title {
      min-height: 40px;
      padding: 0 0 8px !important;
    }

    .ant-table.ant-table-bordered .ant-table-title {
      border: none !important;
    }
  }

  .ant-table {
    width: 100%;
    overflow-x: hidden;
    .action-divider {
      display: table;
      margin: 0;
    }
    &-title {
      display: flex;
      padding: 8px 6px;
      border-bottom: none;
      justify-content: space-between;
      align-items: center;
    }

    //.ant-table-tbody > tr.ant-table-row-selected td {
    //background-color: fade(var(--ant-primary-color), 8%) !important;
    //}
  }

  .ant-pagination {
    margin: 10px 0 0;
  }

  .ant-table-footer {
    padding: 0 !important;

    .ant-table-wrapper {
      padding: 0;
    }

    table {
      border: none !important;
    }

    .ant-table-body {
      overflow-x: hidden !important;
      //  overflow-y: scroll !important;
    }

    td {
      padding: 12px 8px;
    }
  }

  &--inset {
    .ant-table-wrapper {
      padding: 0;
    }
  }
}
//操作列
::v-deep(.ant-table-cell .flex.items-center) {
  display: flex !important;
  justify-content: center !important;
}
</style>
